import axios, { AxiosInstance, AxiosResponse, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig } from "axios";
import { SYSTEM_CONFIG } from "@/config/system";
import {
  objectToUrlParams,
  getAccessToken,
  getRefreshToken,
  setAccessToken,
  setRefreshToken,
  generateUUId,
  messageErrorTip
} from "@whjxtech/jxtech-component";
import { getLocalAppId } from "@/utils/web-platform-config";
import { requestConfigHandle, customErrorResponseHandle } from "@/utils/request-custom";
import { RESULT_CODE } from "@/config/status-code";

const CancelToken = axios.CancelToken;

const service: AxiosInstance = axios.create({
  // 默认地址请求地址，可在 .env.** 文件中修改
  baseURL: SYSTEM_CONFIG.BASE_URL,
  // 设置超时时间
  timeout: 85000,
  // 跨域时候允许携带凭证
  withCredentials: true
});

//存放请求数据
const requestMap = new Map(); // 请求池

let refreshing = false, // 正在刷新标识，避免重复刷新
  waitQueue = [] as any[]; // 请求等待队列

/**
 * token 超时后清理前端token信息 重置路由 并跳转登录页
 */
const tokenInvalidAfter = () => {};

/**
 *  处理请求中字符串有左右空格的情况 均去除
 * @param params 参数
 * @since 2022/05/18 zhu_liangyu
 */
const trimParamHandle = (params: any) => {
  if (!params) return;
  //参数带数组的情况，统一把数组改造成按","分割
  //例如arr[]=1&arr[]=2 改造成 arr=1,2
  for (const key in params) {
    if (Object.hasOwnProperty.call(params, key)) {
      const element = params[key];
      if (Array.isArray(element)) {
        params[key] = element.join(",");
      }
    }
  }
  Object.keys(params).forEach((key, index) => {
    //如果他是字符串 则去掉其左右空格
    if (typeof params[key] === "string") {
      params[key] = params[key].trim();
    }
  });
};

/**
 * 处理重复提交请求
 */
const handleRquestResubmit = {
  before(config: AxiosRequestConfig) {
    const uniqueCode =
      (config.method?.toLowerCase() || "") + config.baseURL + config.url + objectToUrlParams(config.params || {}, true);
    //说明这个请求池中存在这个请求，还在进行中并没有结束
    if (requestMap.get(uniqueCode)) {
      // 防止提交表单等时候重复发起同一个请求（所以这里只限制了post请求）
      if (config.method === "post") {
        // 取消重复请求（取消掉后面重复的请求）直接取消当前的请求，请求还未发出就被取消了，所以浏览器不会生成取消的记录
        config.cancelToken = new CancelToken(cancel => {
          cancel("请不要重新提交请求" + uniqueCode);
        }); //取消的是当前的请求
      }
    } else {
      //存贮请求的唯一标识作为健key: 对应的取消函数作为值 value
      config.cancelToken = new CancelToken(cancel => requestMap.set(uniqueCode, cancel));
    }
  },
  after(response: AxiosResponse<any>) {
    // 剔除请求池中完成的请求
    const uniqueCode =
      (response.config.method?.toLowerCase() || "") +
      response.config.baseURL +
      response.config.url +
      objectToUrlParams(response.config.params || {}, true);

    if (requestMap.get(uniqueCode)) {
      requestMap.delete(uniqueCode);
    }
  }
};

function hanlderBlobError(response: AxiosResponse<any>, config: AxiosRequestConfig) {
  if (response.data.type == "application/json") {
    let reader = new FileReader();
    reader.readAsText(response.data);
    reader.onload = e => {
      let result = "";
      if (e.target && e.target.result) {
        const errorData = JSON.parse(e.target.result as string);
        if (errorData && errorData.message) {
          result += " " + errorData.message;
        }
      }
      messageErrorTip(result);
      console.log(result);
    };
    // loadingText = "导出异常 请稍后再试"
  }
}

//是否为 v2 版本接口的响应
function isV2ApiResponse(response: AxiosResponse) {
  //暂时约定：如果 response.data 包含code、message属性，则认为是新的接口
  return response.data?.hasOwnProperty("message") && response.data?.hasOwnProperty("code");
}

function handleErrorResponse(response: AxiosResponse) {
  const status = isV2ApiResponse(response) ? response.data?.code || response.data?.status : response.status;
  if (status == RESULT_CODE.SERVER_ERROR) {
    //no-error-tip:代表这种错误不需要提示
    // if (response.data.message?.indexOf('no-error-tip:') == -1) {
    //   messageErrorTip('服务器异常：' + response.data.message || '系统内部异常');
    // }
    messageErrorTip("系统异常：" + response.data.message || "系统内部异常");
  } else if (status == RESULT_CODE.UNAUTHORIZED) {
  } else if (status == RESULT_CODE.NOT_FOUND_ERROR) {
    messageErrorTip("接口未找到：" + response.config.url);
  } else if (status == RESULT_CODE.UNAVAILABLE_ERROR) {
    messageErrorTip("系统异常：" + "服务不可用");
  } else if (status == 400) {
    if (!refreshing) messageErrorTip("登录失败 " + (response.data.message || ""));
  } else {
    //其它异常应用系统自行处理
    customErrorResponseHandle(response);
  }
}

/**
 * @description 请求拦截器
 * 客户端发送请求 -> [请求拦截器] -> 服务器
 * token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
 */
service.interceptors.request.use(
  async config => {
    /* ---------------------------------------- zhuliangyu 2024/4/3 优化token失效问题 start  ---------------------------------------- */
    // const expireTimeStr = localStorage.getItem('expireTime');
    // if (expireTimeStr) {
    //   const expireTime = Number(expireTimeStr);
    //   if (new Date().getTime() > expireTime) {
    //     localStorage.removeItem('expireTime');
    //     try {
    //       const refreshRes = await refreshToken(getRefreshToken());
    //       setAccessToken(refreshRes.data.access_token);
    //       setRefreshToken(refreshRes.data.refresh_token);
    //       //存入失效时间
    //       const expireTime = new Date().getTime() + refreshRes.data.expires_in * 1000;
    //       localStorage.setItem('expireTime', String(expireTime));
    //     } catch (err) {
    //       tokenInvalidAfter();
    //     }
    //   }
    // }
    /* ---------------------------------------- zhuliangyu 2024/4/3 优化token失效问题 end  ---------------------------------------- */

    //写入token
    let token = getAccessToken();
    if (!config.headers) config.headers = {};
    config.headers["JxudpAppId"] = getLocalAppId();
    if (token) {
      config.headers["Authorization"] = "bearer " + token;
    }
    if (!config.params) config.params = {};
    config.params._t = new Date().getTime();

    //以下接口不需要传递token
    if (config.url === "/oauth/token" || config.url === "/getSysConfig") {
      delete config.headers.Authorization;
    }
    if (!config.headers["Content-Type"]) {
      config.headers["Content-Type"] = "application/json;charset=UTF-8";
    }
    trimParamHandle(config.params);
    //handleRquestResubmit.before(config);

    //如果开启了链路跟踪则加上 traceId
    if ((window as any).appInfo?.enableLinkTracing) {
      config.headers["traceId"] = generateUUId();
    }

    requestConfigHandle(config);
    return config;
  },
  (error: AxiosError) => {
    return Promise.reject(error);
  }
);

/**
 * @description 响应拦截器
 *  服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
 */
service.interceptors.response.use(
  response => {
    handleRquestResubmit.after(response);

    if (isV2ApiResponse(response)) {
      const v2Response = response.data;
      if (v2Response.code != RESULT_CODE.SERVER_SUCCESS) {
        handleErrorResponse(response);
      }
      return v2Response;
    } else {
      return response;
    }
  },
  error => {
    const response = error.response;
    const config = error.response.config;
    handleRquestResubmit.after(response);
    if (config.responseType == "blob") {
      hanlderBlobError(response, config);
    } else {
      if (response.status == RESULT_CODE.UNAUTHORIZED) {
        let refresh_token = getRefreshToken() as string;
        //令牌失效后 我们准备进行刷新
        if (refresh_token && response.data.path != "/oauth/token") {
          if (refreshing == false) {
            refreshing = true;
            //刷新token
            return refreshToken(refresh_token)
              .then(res => {
                let token = res.data.access_token;
                setAccessToken(token);
                //设置刷新token
                setRefreshToken(res.data.refresh_token);
                waitQueue.forEach(callback => callback(token)); // 已刷新token，所有队列中的请求重试
                waitQueue = [];
                return service(config);
              })
              .catch(() => {
                tokenInvalidAfter();
              })
              .finally(() => {
                refreshing = false;
              });
          } else {
            // 正在刷新token，返回未执行resolve的Promise,刷新token执行回调
            return new Promise(resolve => {
              waitQueue.push(() => {
                //config.headers['Authorization'] = token
                //config.baseURL = '' // 请求重试时，url已包含baseURL
                resolve(service(config));
              });
            });
          }
        } else {
          tokenInvalidAfter();
        }
      } else {
        handleErrorResponse(response);
      }
    }
    return Promise.reject(error);
  }
);

export default service;
